/**
 * @fileOverview 基础类方法。
 */
/**
 * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。
 *
 * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.
 * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：
 *
 * * module `base`：WebUploader.Base
 * * module `file`: WebUploader.File
 * * module `lib/dnd`: WebUploader.Lib.Dnd
 * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd
 *
 *
 * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。
 * @module WebUploader
 * @title WebUploader API文档
 */



var $ = require('./dollar');
var promise = require('./promise');
var noop = function () {
    }, call = Function.call;
// http://jsperf.com/uncurrythis
// 反科里化
function uncurryThis(fn) {
    return function () {
        return call.apply(fn, arguments);
    };
}
function bindFn(fn, context) {
    return function () {
        return fn.apply(context, arguments);
    };
}
function createObject(proto) {
    var f;
    if (Object.create) {
        return Object.create(proto);
    } else {
        f = function () {
        };
        f.prototype = proto;
        return new f();
    }
}
module.exports = {
    /**
         * @property {String} version 当前版本号。
         */
    version: '@version@',
    /**
         * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。
         */
    $: $,
    Deferred: promise.Deferred,
    isPromise: promise.isPromise,
    when: promise.when,
    /**
         * @description  简单的浏览器检查结果。
         *
         * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。
         * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。
         * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**
         * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。
         * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。
         * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。
         *
         * @property {Object} [browser]
         */
    browser: function (ua) {
        var ret = {}, webkit = ua.match(/WebKit\/([\d.]+)/), chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/), ie = ua.match(/MSIE\s([\d\.]+)/) || ua.match(/(?:trident)(?:.*rv:([\w.]+))?/i), firefox = ua.match(/Firefox\/([\d.]+)/), safari = ua.match(/Safari\/([\d.]+)/), opera = ua.match(/OPR\/([\d.]+)/);
        webkit && (ret.webkit = parseFloat(webkit[1]));
        chrome && (ret.chrome = parseFloat(chrome[1]));
        ie && (ret.ie = parseFloat(ie[1]));
        firefox && (ret.firefox = parseFloat(firefox[1]));
        safari && (ret.safari = parseFloat(safari[1]));
        opera && (ret.opera = parseFloat(opera[1]));
        return ret;
    }(navigator.userAgent),
    /**
         * @description  操作系统检查结果。
         *
         * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。
         * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。
         * @property {Object} [os]
         */
    os: function (ua) {
        var ret = {},
            // osx = !!ua.match( /\(Macintosh\; Intel / ),
            android = ua.match(/(?:Android);?[\s\/]+([\d.]+)?/), ios = ua.match(/(?:iPad|iPod|iPhone).*OS\s([\d_]+)/);
        // osx && (ret.osx = true);
        android && (ret.android = parseFloat(android[1]));
        ios && (ret.ios = parseFloat(ios[1].replace(/_/g, '.')));
        return ret;
    }(navigator.userAgent),
    /**
         * 实现类与类之间的继承。
         * @method inherits
         * @grammar Base.inherits( super ) => child
         * @grammar Base.inherits( super, protos ) => child
         * @grammar Base.inherits( super, protos, statics ) => child
         * @param  {Class} super 父类
         * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。
         * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。
         * @param  {Object} [statics] 静态属性或方法。
         * @return {Class} 返回子类。
         * @example
         * function Person() {
         *     console.log( 'Super' );
         * }
         * Person.prototype.hello = function() {
         *     console.log( 'hello' );
         * };
         *
         * var Manager = Base.inherits( Person, {
         *     world: function() {
         *         console.log( 'World' );
         *     }
         * });
         *
         * // 因为没有指定构造器，父类的构造器将会执行。
         * var instance = new Manager();    // => Super
         *
         * // 继承子父类的方法
         * instance.hello();    // => hello
         * instance.world();    // => World
         *
         * // 子类的__super__属性指向父类
         * console.log( Manager.__super__ === Person );    // => true
         */
    inherits: function (Super, protos, staticProtos) {
        var child;
        if (typeof protos === 'function') {
            child = protos;
            protos = null;
        } else if (protos && protos.hasOwnProperty('constructor')) {
            child = protos.constructor;
        } else {
            child = function () {
                return Super.apply(this, arguments);
            };
        }
        // 复制静态方法
        $.extend(true, child, Super, staticProtos || {});
        /* jshint camelcase: false */
        // 让子类的__super__属性指向父类。
        child.__super__ = Super.prototype;
        // 构建原型，添加原型方法或属性。
        // 暂时用Object.create实现。
        child.prototype = createObject(Super.prototype);
        protos && $.extend(true, child.prototype, protos);
        return child;
    },
    /**
         * 一个不做任何事情的方法。可以用来赋值给默认的callback.
         * @method noop
         */
    noop: noop,
    /**
         * 返回一个新的方法，此方法将已指定的`context`来执行。
         * @grammar Base.bindFn( fn, context ) => Function
         * @method bindFn
         * @example
         * var doSomething = function() {
         *         console.log( this.name );
         *     },
         *     obj = {
         *         name: 'Object Name'
         *     },
         *     aliasFn = Base.bind( doSomething, obj );
         *
         *  aliasFn();    // => Object Name
         *
         */
    bindFn: bindFn,
    /**
         * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。
         * @grammar Base.log( args... ) => undefined
         * @method log
         */
    log: function () {
        if (window.console) {
            return bindFn(console.log, console);
        }
        return noop;
    }(),
    nextTick: function () {
        return function (cb) {
            setTimeout(cb, 1);
        };    // @bug 当浏览器不在当前窗口时就停了。
              // var next = window.requestAnimationFrame ||
              //     window.webkitRequestAnimationFrame ||
              //     window.mozRequestAnimationFrame ||
              //     function( cb ) {
              //         window.setTimeout( cb, 1000 / 60 );
              //     };
              // // fix: Uncaught TypeError: Illegal invocation
              // return bindFn( next, window );
    }(),
    /**
         * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。
         * 将用来将非数组对象转化成数组对象。
         * @grammar Base.slice( target, start[, end] ) => Array
         * @method slice
         * @example
         * function doSomthing() {
         *     var args = Base.slice( arguments, 1 );
         *     console.log( args );
         * }
         *
         * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array ["arg2", "arg3"]
         */
    slice: uncurryThis([].slice),
    /**
         * 生成唯一的ID
         * @method guid
         * @grammar Base.guid() => String
         * @grammar Base.guid( prefx ) => String
         */
    guid: function () {
        var counter = 0;
        return function (prefix) {
            var guid = (+new Date()).toString(32), i = 0;
            for (; i < 5; i++) {
                guid += Math.floor(Math.random() * 65535).toString(32);
            }
            return (prefix || 'wu_') + guid + (counter++).toString(32);
        };
    }(),
    /**
         * 格式化文件大小, 输出成带单位的字符串
         * @method formatSize
         * @grammar Base.formatSize( size ) => String
         * @grammar Base.formatSize( size, pointLength ) => String
         * @grammar Base.formatSize( size, pointLength, units ) => String
         * @param {Number} size 文件大小
         * @param {Number} [pointLength=2] 精确到的小数点数。
         * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
         * @example
         * console.log( Base.formatSize( 100 ) );    // => 100B
         * console.log( Base.formatSize( 1024 ) );    // => 1.00K
         * console.log( Base.formatSize( 1024, 0 ) );    // => 1K
         * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M
         * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G
         * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB
         */
    formatSize: function (size, pointLength, units) {
        var unit;
        units = units || [
            'B',
            'K',
            'M',
            'G',
            'TB'
        ];
        while ((unit = units.shift()) && size > 1024) {
            size = size / 1024;
        }
        return (unit === 'B' ? size : size.toFixed(pointLength || 2)) + unit;
    }
} || module.exports;;